#include "test_common.h"
/*============================================================================*
 *                                  Local                                     *
 *============================================================================*/
static void add_messages(void)
{
	Ender_Namespace *ns;
	Ender_Descriptor *d;
	Ender_Container *c;
	Ender_Property *p;

	/* first the welcome */
	ns = ender_namespace_new("Test", 0);
	d = ender_namespace_descriptor_add(
			ns,
			"Test_Message_Welcome",
			NULL,
			NULL,
			NULL,
			ENDER_TYPE_STRUCT, sizeof(Test_Message_Welcome));
	c = ender_container_new(ENDER_INT32);
	p = ender_descriptor_struct_property_add(d, "i", c,
			offsetof(Test_Message_Welcome, i));
	TEST_MESSAGE_WELCOME = eix_message_add(d, NULL, 0);

	/* now the answer */
	d = ender_namespace_descriptor_add(
			ns,
			"Test_Message_Answer",
			NULL,
			NULL,
			NULL,
			ENDER_TYPE_STRUCT, sizeof(Test_Message_Answer));
	c = ender_container_new(ENDER_STRING);
	p = ender_descriptor_struct_property_add(d, "s", c,
			offsetof(Test_Message_Answer, s));
	TEST_MESSAGE_ANSWER = eix_message_add(d, NULL, 0);
	/* now the question, this messages is a sync message and expects
	 * a reply
	 */
	d = ender_namespace_descriptor_add(
			ns,
			"Test_Message_Question",
			NULL,
			NULL,
			NULL,
			ENDER_TYPE_STRUCT, sizeof(Test_Message_Question));
	c = ender_container_new(ENDER_STRING);
	p = ender_descriptor_struct_property_add(d, "s", c,
			offsetof(Test_Message_Question, s));
	TEST_MESSAGE_QUESTION = eix_message_add(d, TEST_MESSAGE_ANSWER, 0);
	/* now the ack, this message does not have a payload, i.e signal */
	TEST_MESSAGE_ACK = eix_message_add(NULL, NULL, 0);
}
/*----------------------------------------------------------------------------*
 *                            Transport processing                            *
 *----------------------------------------------------------------------------*/
/* The thing here is that given that we are not using any main loop but we
 * want to process the async sockets, we need to provide our own "process"
 * function, which will just select() on the fd, check for the fds set for
 * reading/writing and call the transport function appropriately
 */

static void _transport_process(Eix_Transport *t, Eina_Bool *can_read, Eina_Bool *can_write)
{
	Eix_Transport_State state;
	Eina_Bool needs_read = EINA_FALSE, needs_write = EINA_FALSE;
	Eina_Bool serves;
 	fd_set readset, writeset;
	struct timeval tv;
 	int ret;
	int fd;

	/* initialize */
	*can_read = EINA_FALSE;
	*can_write = EINA_FALSE;

	state = eix_transport_state_get(t);
	fd = eix_transport_unix_tcp_fd_get(t);
	/* check where to we want to process */
	eix_transport_process_required(t, &needs_read, &needs_write);
	if (!needs_read && !needs_write) return;

	/* set the time to zero to return immediately */
	tv.tv_sec = 0;
	tv.tv_usec = 0;

	FD_ZERO(&readset);
	FD_ZERO(&writeset);
	if (needs_read)
		FD_SET(fd, &readset);
	if (needs_write)
		FD_SET(fd, &writeset);

	if (select(fd + 1, &readset, &writeset, NULL, &tv) <= 0)
		return;

	if (FD_ISSET(fd, &writeset))
		*can_write = EINA_TRUE;
	if (FD_ISSET(fd, &readset))
		*can_read = EINA_TRUE;
}

/*----------------------------------------------------------------------------*
 *                            The server interface                            *
 *----------------------------------------------------------------------------*/
/* client callbacks */
static void _client_server_add(Eix_Server *srv, void *socket, void *user_data)
{
	Test_Message_Welcome msg;

	printf("TC: Server added\n");
}

static void _client_server_del(Eix_Server *src, void *socket, void *user_data)
{
	printf("TC: Server removed\n");
	test_end = EINA_TRUE;
	test_connected = EINA_FALSE;
}

static void _client_server_data(Eix_Server *srv, void *socket,
		const Eix_Message *m, void *data,
		void *reply, Eix_Error *err, void *user_data)
{
	printf("TC: Server data\n");
	/* check the type of message */
	if (m == TEST_MESSAGE_WELCOME)
	{
		Test_Message_Welcome *msg = data;

		printf("TC: Server sent a 'welcome' message for %d %p\n", msg->i, msg);
		test_connected = EINA_TRUE;
	}
	else if (m == TEST_MESSAGE_ACK)
	{
		printf("TC: Server sent an 'ack' message\n");

	}
	else
	{
		printf("TC: Unknown message %p\n", m);
		return;
	}
}

static Eix_Server_Descriptor _client_descriptor = {
	/* .add 	= */ _client_server_add,
	/* .del 	= */ _client_server_del,
	/* .data 	= */ _client_server_data,
};
/*----------------------------------------------------------------------------*
 *                            The server interface                            *
 *----------------------------------------------------------------------------*/
/* server callbacks */
static void _server_client_add(Eix_Server *srv, void *socket, void *user_data)
{
	Eix_Client *c = socket;
	Test_Message_Welcome msg;
	static int count = 0;

	test_connected = EINA_TRUE;
	/* whenever a client is connected send a welcome message */
	/* create a test message and send it */
	msg.i = count++;

	printf("TS: Client added %p\n", c);
	printf("TS: Sending message: i = '%d'\n", msg.i);
	/* we need to process this client in order to receive its messages */
	eix_client_message_send(c, TEST_MESSAGE_WELCOME, &msg, NULL, NULL, NULL);
}

static void _server_client_del(Eix_Server *srv, void *socket, void *user_data)
{
	printf("TS: Client deleted\n");
}

static void _server_client_data(Eix_Server *srv, void *socket,
		const Eix_Message *m, void *data,
		void *reply, Eix_Error *err, void *user_data)
{
	Eix_Client *c = socket;
	Test_Message_Answer *q = data;

	printf("TS: Client data\n");
	/* check if it is our expected message */
	if (m == TEST_MESSAGE_QUESTION)
	{
		Test_Message_Answer *a = reply;

		/* everytime we receive a question, before answering
		 * we first send an ack, to force the code of the library
		 * to accumulate it */
		eix_client_message_send(c, TEST_MESSAGE_ACK, NULL, NULL, NULL, NULL);
		printf("TS: Received question: '%s'\n", q->s);
		printf("TS: Answering 'ok'\n");
		if (!strcmp(q->s, "Who are you?"))
		{
			a->s = "The server";
		}
		else
		{
			eix_error_set(err, EIX_ERR_CODEC, "Unknown question");
		}
	}
	else
	{
		printf("TS: Uknown message\n");
		/* in case the client sends another message beside the question
		 * and if it expects an error/reply, set it */
		if (err)
		{
			eix_error_set(err, EIX_ERR_NEXIST, "Unknown message");
		}
	}
}

static Eix_Server_Descriptor _server_descriptor = {
	/* .add 	= */ _server_client_add,
	/* .del 	= */ _server_client_del,
	/* .data 	= */ _server_client_data,
};

/*============================================================================*
 *                                 Global                                     *
 *============================================================================*/
Eix_Message *TEST_MESSAGE_WELCOME = NULL;
Eix_Message *TEST_MESSAGE_QUESTION = NULL;
Eix_Message *TEST_MESSAGE_ANSWER = NULL;
Eix_Message *TEST_MESSAGE_BYE = NULL;
Eix_Message *TEST_MESSAGE_ACK = NULL;
Eina_Bool test_end = EINA_FALSE;
Eina_Bool test_connected = EINA_FALSE;

void test_common_init(void)
{
	eix_init();
	add_messages();
}

void test_common_shutdown(void)
{
	eix_shutdown();
}

Eix_Server * test_common_connect(void)
{
	Eix_Transport *t;
	Eix_Server *srv;

	printf("TC: Creating the client\n");
	t = eix_transport_unix_tcp_new(UNIX_PATH);
	srv = eix_server_connect(t, &_client_descriptor, NULL);
	if (!srv)
	{
		printf("TC: Impossible to connect\n");
	}
	return srv;
}

Eix_Server * test_common_serve(void)
{
	Eix_Transport *t;
	Eix_Server *srv;

	printf("TS: Creating the server\n");
	t = eix_transport_unix_tcp_new(UNIX_PATH);
	srv = eix_server_new(t, &_server_descriptor, NULL);
	return srv;
}

void test_common_server_process(Eix_Server *s)
{
	Eina_Bool can_read, can_write;
	Eix_Client *c;
	Eix_Transport *t;
	const Eina_List *clients, *l;

	if (!s)
	{
		test_end = EINA_TRUE;
		return;
	}

	t = eix_server_transport_get(s);
	if (!t)
	{
		test_end = EINA_TRUE;
		return;
	}

	_transport_process(t, &can_read, &can_write);
	eix_server_process(s, can_read, can_write);

	/* now process the connected children */
	clients = eix_server_client_get(s);
	//printf("server processing %p\n", s);
	EINA_LIST_FOREACH (clients, l, c)
	{
		//printf("client processing %p\n", c);
		t = eix_client_transport_get(c);
		_transport_process(t, &can_read, &can_write);
		eix_client_process(c, can_read, can_write);
	}
}

